<!DOCTYPE html>
<html lang="en-US">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>3. 把页面拼成个Web应用 | 被删的前端游乐场</title>
    <meta name="generator" content="VuePress 1.8.2">
    
    <meta name="description" content="Just playing around">
    
    <link rel="preload" href="/front-end-playground/assets/css/0.styles.6ad2a9ca.css" as="style"><link rel="preload" href="/front-end-playground/assets/js/app.1e2670bf.js" as="script"><link rel="preload" href="/front-end-playground/assets/js/2.38d016d1.js" as="script"><link rel="preload" href="/front-end-playground/assets/js/3.e3f029cb.js" as="script"><link rel="preload" href="/front-end-playground/assets/js/142.b26ccf3b.js" as="script">
    <link rel="stylesheet" href="/front-end-playground/assets/css/0.styles.6ad2a9ca.css">
  </head>
  <body>
    <div id="app" data-server-rendered="true"><div class="theme-container"><header class="navbar"><div class="sidebar-button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" role="img" viewBox="0 0 448 512" class="icon"><path fill="currentColor" d="M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z"></path></svg></div> <a href="/front-end-playground/" class="home-link router-link-active"><!----> <span class="site-name">被删的前端游乐场</span></a> <div class="links"><div class="search-box"><input aria-label="Search" autocomplete="off" spellcheck="false" value=""> <!----></div> <nav class="nav-links can-hide"><div class="nav-item"><a href="/front-end-playground/" class="nav-link">概述</a></div><div class="nav-item"><a href="/front-end-playground/front-end-basic/" class="nav-link">前端领域</a></div><div class="nav-item"><a href="/front-end-playground/vue/" class="nav-link router-link-active">Vue学习</a></div><div class="nav-item"><a href="/front-end-playground/wxapp/" class="nav-link">小程序学习</a></div><div class="nav-item"><a href="/front-end-playground/front-end-others/" class="nav-link">百家齐放</a></div><div class="nav-item"><a href="/front-end-playground/front-end-addon/" class="nav-link">前端的进击</a></div><div class="nav-item"><a href="/front-end-playground/front-end-work/" class="nav-link">前端与工作</a></div><div class="nav-item"><a href="/front-end-playground/faq.html" class="nav-link">FAQ</a></div> <a href="https://github.com/godbasin/front-end-playground" target="_blank" rel="noopener noreferrer" class="repo-link">
    Github
    <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></nav></div></header> <div class="sidebar-mask"></div> <aside class="sidebar"><nav class="nav-links"><div class="nav-item"><a href="/front-end-playground/" class="nav-link">概述</a></div><div class="nav-item"><a href="/front-end-playground/front-end-basic/" class="nav-link">前端领域</a></div><div class="nav-item"><a href="/front-end-playground/vue/" class="nav-link router-link-active">Vue学习</a></div><div class="nav-item"><a href="/front-end-playground/wxapp/" class="nav-link">小程序学习</a></div><div class="nav-item"><a href="/front-end-playground/front-end-others/" class="nav-link">百家齐放</a></div><div class="nav-item"><a href="/front-end-playground/front-end-addon/" class="nav-link">前端的进击</a></div><div class="nav-item"><a href="/front-end-playground/front-end-work/" class="nav-link">前端与工作</a></div><div class="nav-item"><a href="/front-end-playground/faq.html" class="nav-link">FAQ</a></div> <a href="https://github.com/godbasin/front-end-playground" target="_blank" rel="noopener noreferrer" class="repo-link">
    Github
    <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></nav>  <ul class="sidebar-links"><li><section class="sidebar-group collapsable depth-0" style="padding-top:10px;"><div class="kitty-main" data-v-2b653b36><span class="stand" data-v-2b653b36></span> <div class="cat" data-v-2b653b36><div class="body" data-v-2b653b36></div> <div class="head" data-v-2b653b36><div class="ear" data-v-2b653b36></div> <div class="ear" data-v-2b653b36></div></div> <div class="face" data-v-2b653b36><div class="nose" data-v-2b653b36></div> <div class="whisker-container" data-v-2b653b36><div class="whisker" data-v-2b653b36></div> <div class="whisker" data-v-2b653b36></div></div> <div class="whisker-container" data-v-2b653b36><div class="whisker" data-v-2b653b36></div> <div class="whisker" data-v-2b653b36></div></div></div> <div class="tail-container" data-v-2b653b36><div class="tail" data-v-2b653b36><div class="tail" data-v-2b653b36><div class="tail" data-v-2b653b36><div class="tail" data-v-2b653b36><div class="tail" data-v-2b653b36><div class="tail" data-v-2b653b36><div class="tail" data-v-2b653b36></div></div></div></div></div></div></div></div></div></div> <p class="sidebar-heading open"><span>9102 全员学 Vue</span> <span class="arrow down"></span></p> <ul class="sidebar-links sidebar-group-items"><li><a href="/front-end-playground/vue/vue-for-everyone/vue-for-everyone-1.html" class="sidebar-link">1. 如何理解前端和 Vue</a></li><li><a href="/front-end-playground/vue/vue-for-everyone/vue-for-everyone-2.html" class="sidebar-link">2. 怎么三两下拼出一个页面</a></li><li><a href="/front-end-playground/vue/vue-for-everyone/vue-for-everyone-3.html" aria-current="page" class="active sidebar-link">3. 把页面拼成个Web应用</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a href="/front-end-playground/vue/vue-for-everyone/vue-for-everyone-3.html#spa-与-mpa" class="sidebar-link">SPA 与 MPA</a></li><li class="sidebar-sub-header"><a href="/front-end-playground/vue/vue-for-everyone/vue-for-everyone-3.html#其他应用" class="sidebar-link">其他应用</a></li><li class="sidebar-sub-header"><a href="/front-end-playground/vue/vue-for-everyone/vue-for-everyone-3.html#页面结构设计" class="sidebar-link">页面结构设计</a></li><li class="sidebar-sub-header"><a href="/front-end-playground/vue/vue-for-everyone/vue-for-everyone-3.html#页面路由设计" class="sidebar-link">页面路由设计</a></li><li class="sidebar-sub-header"><a href="/front-end-playground/vue/vue-for-everyone/vue-for-everyone-3.html#目录结构划分" class="sidebar-link">目录结构划分</a></li><li class="sidebar-sub-header"><a href="/front-end-playground/vue/vue-for-everyone/vue-for-everyone-3.html#使用开源库和工具" class="sidebar-link">使用开源库和工具</a></li><li class="sidebar-sub-header"><a href="/front-end-playground/vue/vue-for-everyone/vue-for-everyone-3.html#安装和引入-vue-router" class="sidebar-link">安装和引入 vue-router</a></li><li class="sidebar-sub-header"><a href="/front-end-playground/vue/vue-for-everyone/vue-for-everyone-3.html#使用-vue-router-添加路由" class="sidebar-link">使用 vue-router 添加路由</a></li><li class="sidebar-sub-header"><a href="/front-end-playground/vue/vue-for-everyone/vue-for-everyone-3.html#给路由添加鉴权" class="sidebar-link">给路由添加鉴权</a></li></ul></li></ul></section></li><li><section class="sidebar-group collapsable depth-0" style="padding-top:;"><!----> <p class="sidebar-heading"><span>深入理解 Vue 动画效果</span> <span class="arrow right"></span></p> <!----></section></li><li><section class="sidebar-group collapsable depth-0" style="padding-top:;"><!----> <p class="sidebar-heading"><span>Webpack 实现 Vue 多页应用</span> <span class="arrow right"></span></p> <!----></section></li><li><a href="http://www.godbasin.com/vue-ebook/" target="_blank" rel="noopener noreferrer" class="sidebar-link">深入理解Vue.js与实战<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li></ul> </aside> <main class="page"> <div class="theme-default-content content__default"><p>本节内容主要介绍使用 vue-router 来管理路由，包括应用和路由的设计和配置、页面间跳转、路由鉴权等。然后将上一节拼出来的页面，再拼出来一个完整的 Web 应用叭。</p> <p>本节内容包括：</p> <ul><li>前端单应用介绍</li> <li>vue-router 使用介绍</li> <li>页面划分、路由设计、路由监听</li> <li>如何进行路由鉴权</li></ul> <p><a href="http://vue-for-everyone.godbasin.com/3/index.html" target="_blank" rel="noopener noreferrer">点击查看最终页面的效果<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a>：登录页面随便输入内容就可以</p> <p>我们<a href="/front-end-playground/vue/vue-for-everyone/vue-for-everyone-1.html">第一节内容</a>里，最后用<a href="https://cli.vuejs.org/zh/" target="_blank" rel="noopener noreferrer">Vue CLI<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a>生成了一个 demo，我们称之为一个 Web 应用。</p> <h1 id="单页应用"><a href="#单页应用" class="header-anchor">#</a> 单页应用</h1> <p>单页应用这个概念，是随着前几年 AngularJS、React、Ember 等这些框架的出现而出现的。<a href="/front-end-playground/vue/vue-for-everyone/vue-for-everyone-1.html">第一节内容</a>里，我们在页面渲染中讲了页面的局部刷新，而单页应用则是使用了页面的局部刷新的能力，在切换页面的时候刷新页面内容，从而获取更好的体验。</p> <h2 id="spa-与-mpa"><a href="#spa-与-mpa" class="header-anchor">#</a> SPA 与 MPA</h2> <p>单页应用（SinglePage Web Application，SPA）和多页应用（MultiPage Application，MPA）的区别可以参考：</p> <table><thead><tr><th>-</th> <th>单页面应用</th> <th>多页面应用</th></tr></thead> <tbody><tr><td>组成</td> <td>一个外壳页面和多个页面片段组成</td> <td>多个完整页面构成</td></tr> <tr><td>资源共用(css,js)</td> <td>共用，只需在外壳部分加载</td> <td>不共用，每个页面都需要加载</td></tr> <tr><td>刷新方式</td> <td>页面局部刷新或更改</td> <td>整页刷新</td></tr> <tr><td>url 模式</td> <td>a.com/#/pageone<br>a.com/#/pagetwo</td> <td>a.com/pageone.html<br>a.com/pagetwo.html</td></tr> <tr><td>用户体验</td> <td>页面片段间的切换快，用户体验良好</td> <td>页面切换加载缓慢，流畅度不够，用户体验比较差</td></tr> <tr><td>页面跳转动画</td> <td>容易实现</td> <td>无法实现</td></tr> <tr><td>数据传递</td> <td>容易</td> <td>依赖 url 传参、或者 cookie 、localStorage 等</td></tr> <tr><td>搜索引擎优化(SEO)</td> <td>需要单独方案、实现较为困难、不利于 SEO 检索 可利用服务器端渲染(SSR)优化</td> <td>实现方法简易</td></tr></tbody></table> <blockquote><p>以上表格内容来自<a href="https://juejin.im/post/5a0ea4ec6fb9a0450407725c" target="_blank" rel="noopener noreferrer">《前端：你要懂的单页面应用和多页面应用》<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a>，个人认为整理得挺到位的。</p></blockquote> <h2 id="其他应用"><a href="#其他应用" class="header-anchor">#</a> 其他应用</h2> <p>所以其实可以看到，应用和页面的关系，可以简单地理解为多个页面拼成一个应用的关系。应用也分很多种噢，前端里最常见的是浏览器中的 Web 应用，除了这个以外，还有<a href="https://developer.mozilla.org/zh-CN/docs/Web/Progressive_web_apps" target="_blank" rel="noopener noreferrer">渐进式 Web 应用（Progressive Web Apps，PWA）<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a>，以及原生应用、移动应用。原生应用这些年也有很火的框架出现，像 React Native，Flutter 和 Xamarin 这样的框架允许使用不同于每种标准本地语言的语言来开发所有平台的本地应用程序。</p> <p>从产品的角度来说，一个应用可以理解为给用户提供一套完整的定向服务，会包括用户登录、服务分类（Tab）、交互设计和内容展示等等。从工程项目的角度来看，包括用户身份管理、页面管理、路由管理、应用状态管理等。</p> <p>这一节内容，我们主要来讲路由管理，也就是会把<a href="/front-end-playground/vue/vue-for-everyone/vue-for-everyone-2.html">上一节内容</a>中拼好的一个个页面，串成一个完整的应用。</p> <blockquote><p>原谅我脑袋里忽然想起了小虎队的“把你的心我的心，串一串，串一株幸运草，串一个同心圆”</p></blockquote> <h1 id="页面划分"><a href="#页面划分" class="header-anchor">#</a> 页面划分</h1> <p>在直接讲我们的路由怎么配置前，我们需要先知道我们的应用要怎么划分，路由和页面路径是一一对应的，所以我们需要先设计应用的页面逻辑。我们要知道怎么设计一个应用，或者说根据已有的产品、设计交互，怎么规划我们项目的结构。</p> <p>我们看看上一节的<a href="http://vue-for-everyone.godbasin.com/2/index.html" target="_blank" rel="noopener noreferrer">页面效果<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a>：
<img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/vue-for-everyone-2-1.jpg" alt=""></p> <h2 id="页面结构设计"><a href="#页面结构设计" class="header-anchor">#</a> 页面结构设计</h2> <p>这是常用的一种管理端页面结构，我们可以基于这样的页面设计几种类型的页面拼装：</p> <table><thead><tr><th>序号</th> <th>页面形式</th> <th>页面能力</th></tr></thead> <tbody><tr><td>1</td> <td>登录页</td> <td>只有用户名和密码的输入</td></tr> <tr><td>2</td> <td>列表 + 表单</td> <td>单页可以完成某类服务的增删查改</td></tr> <tr><td>3</td> <td>列表页</td> <td>只有列表展示，提供查和删服务，需要配合 4 的表单页完成增和改</td></tr> <tr><td>4</td> <td>表单页</td> <td>只有表单编辑内容，可提供新增、修改等能力给 3 使用</td></tr></tbody></table> <p>上述 2-4 结构的页面，可以配合路由，整理出这样的菜单信息：
<img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/vue-for-everyone-3-2.jpg" alt=""></p> <h2 id="页面路由设计"><a href="#页面路由设计" class="header-anchor">#</a> 页面路由设计</h2> <p>上述情况下，以<code>/</code>作为根路由（对应的组件为 App.vue），我们设计这么几种路由和页面：</p> <table><thead><tr><th>路由</th> <th>页面内容</th> <th>页面对应的 Component</th> <th>页面组成</th></tr></thead> <tbody><tr><td><code>/login</code></td> <td>登录页</td> <td>Login</td> <td>表单，包括<code>username</code>和<code>password</code></td></tr> <tr><td><code>/home</code></td> <td>应用首页</td> <td>Home</td> <td>左侧菜单<code>&lt;Menu&gt;</code>，右侧路由内容<code>&lt;router-view&gt;</code></td></tr> <tr><td><code>/home/service</code></td> <td>服务信息页</td> <td>Service</td> <td>为 Home 的子路由组件，包括列表和表单</td></tr> <tr><td><code>/home/product</code></td> <td>产品容器页</td> <td>Product</td> <td>为 Home 的子路由组件，包括<code>&lt;router-view&gt;</code></td></tr> <tr><td><code>/home/product/list</code></td> <td>产品信息页</td> <td>ProductList</td> <td>为 Product 的子路由组件，包括列表</td></tr> <tr><td><code>/home/product/edit</code></td> <td>产品编辑页</td> <td>ProductEdit</td> <td>为 Product 的子路由组件，包括表单</td></tr></tbody></table> <p>页面结构和路由嵌套管理，其实是这样的：</p> <div class="language-cmd extra-class"><pre class="language-text"><code>/login                     /home                     /home/service
+------------------+       +-----------------+       +-----------------+
| App              |       | App             |       | App             |
| +--------------+ |       | +-------------+ |       | +-------------+ |
| | Login        | |       | | Home        | |       | | Home        | |
| |              | |       | |             | |       | | +---------+ | |
| |              | |  +--) | |&lt;router-view&gt;| |  +--) | | | Service | | |
| |              | |       | |  无对应内容  | |       | | |列表+表单 | | |
| |              | |       | |             | |       | | +---------+ | |
| +--------------+ |       | +-------------+ |       | +-------------+ |
+------------------+       +-----------------+       +-----------------+


      /home/product                /home/product/list              /home/product/edit
      +---------------------+      +------------------------+      +------------------------+
      | App                 |      | App                    |      | App                    |
      | +-----------------+ |      | +--------------------+ |      | +--------------------+ |
      | | Home            | |      | | Home               | |      | | Home               | |
      | | +-------------+ | |      | | +----------------+ | |      | | +----------------+ | |
 +--) | | | Product     | | | +--) | | | Product        | | | +--) | | | Product        | | |
      | | |&lt;router-view&gt;| | |      | | | +------------+ | | |      | | | +------------+ | | |
      | | |  无对应内容  | | |      | | | | ProductList| | | |      | | | | ProductEdit| | | |
      | | |             | | |      | | | | 单列表页    | | | |      | | | | 单表单页    | | | |
      | | |             | | |      | | | +------------+ | | |      | | | +------------+ | | |
      | | +-------------+ | |      | | +----------------+ | |      | | +----------------+ | |
      | +-----------------+ |      | +--------------------+ |      | +--------------------+ |
      +---------------------+      +------------------------+      +------------------------+

      (=′∇`=）画这个图累死俺了
</code></pre></div><p>我们能看到，这里包括了层层的路由嵌套关系，我们后面在配置路由的时候也能看到这样的结构。</p> <h2 id="目录结构划分"><a href="#目录结构划分" class="header-anchor">#</a> 目录结构划分</h2> <p>我们看到上面的路由划分示意图，使用框框框起来的代表一个 Vue component，而在 Vue 中，其实一切皆组件（页面是特殊的组件），那我们要怎么区分页面和组件呢，一般可以使用项目目录来简单做一些划分：</p> <div class="language-cmd extra-class"><pre class="language-text"><code>├─dist                      // 编译之后的项目文件
├─src                       // 开发目录
│  ├─assets                 // 静态资源
│     ├─less                // 公共less
│     ├─img                 // 图片资源
│  ├─components             // **放这里是组件**
│  ├─pages                  // **放这里是页面** 根据路由结构划分
│  ├─utils                  // 工具库
│  ├─App.vue                // 启动页面，最外层容器组件
│  ├─main.js                // 入口脚本
├─babel.config.js          // babel 配置文件
├─vue.config.js            // vue 自定义配置，与 webpack 配置相关
├─package.json             // 项目配置
├─README.md                // 项目说明
</code></pre></div><p>目录结构清晰，其实对一个项目的可维护性非常重要，一眼看去你就知道这个项目大概包括了哪些内容，分别都放在哪里。好看的目录结构和命名，和好看的代码结构和命名一样，已经是天然的说明了，这是很好的编码习惯呢。</p> <p>好了，项目目录和路由结构我们划分好了，我们来看看怎么根据上面的设计来配置路由，以及实现相互跳转吧。</p> <h1 id="路由配置"><a href="#路由配置" class="header-anchor">#</a> 路由配置</h1> <p>Vue 框架本身的定位是核心关注视图层，所以路由配置、状态管理、其他千奇百怪的测试、mock 功能等都不是自带的，我们需要自己找到对应的开源库配合使用（像 angular 这种大而全的框架，则会提供了开箱即用的完整功能，适合大型项目使用）。例如与 Vue 结合的路由管理，一般选择 <a href="https://router.vuejs.org/zh/" target="_blank" rel="noopener noreferrer">vue-router<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a>。</p> <h2 id="使用开源库和工具"><a href="#使用开源库和工具" class="header-anchor">#</a> 使用开源库和工具</h2> <p>这里顺便介绍下前端怎么找开源库和工具，包括几种途径：</p> <ol><li>google 搜 <code>vue router</code>。（没有爬墙工具的小伙伴，可以使用 <a href="https://cn.bing.com/" target="_blank" rel="noopener noreferrer">bing<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a> 噢）</li> <li>在 <a href="https://github.com/" target="_blank" rel="noopener noreferrer">github<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a> 上搜 <code>vue router</code>。</li> <li>去 <a href="https://www.npmjs.com/" target="_blank" rel="noopener noreferrer">npm<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a> 上找 <code>vue router</code> 资源包。（因为前端开源都是基于 npm 包管理，所以基本上都能在 npm 里找到）</li></ol> <p>如图：
<img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/vue-for-everyone-3-1.jpg" alt="搜索开源库和工具"></p> <h2 id="安装和引入-vue-router"><a href="#安装和引入-vue-router" class="header-anchor">#</a> 安装和引入 vue-router</h2> <p>其实官方文档也有介绍怎么安装和使用，这里再简单介绍一下哈：</p> <h3 id="_1-安装依赖"><a href="#_1-安装依赖" class="header-anchor">#</a> 1. 安装依赖</h3> <div class="language-cmd extra-class"><pre class="language-text"><code># 安装依赖
npm install vue-router
</code></pre></div><h3 id="_2-引入-vue-router"><a href="#_2-引入-vue-router" class="header-anchor">#</a> 2. 引入 vue-router</h3> <div class="language-js extra-class"><pre class="language-js"><code><span class="token comment">// main.js</span>
<span class="token keyword">import</span> Vue <span class="token keyword">from</span> <span class="token string">&quot;vue&quot;</span><span class="token punctuation">;</span>
<span class="token comment">// 引入 vue-router</span>
<span class="token keyword">import</span> VueRouter <span class="token keyword">from</span> <span class="token string">&quot;vue-router&quot;</span><span class="token punctuation">;</span>
Vue<span class="token punctuation">.</span><span class="token function">use</span><span class="token punctuation">(</span>VueRouter<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 使用 vue-router</span>
</code></pre></div><h2 id="使用-vue-router-添加路由"><a href="#使用-vue-router-添加路由" class="header-anchor">#</a> 使用 vue-router 添加路由</h2> <p>这里我们根据前面的应用设计，讲一下要怎么配置，vue-router 怎么使用。</p> <h3 id="_1-配置路由信息"><a href="#_1-配置路由信息" class="header-anchor">#</a> 1. 配置路由信息</h3> <p>根据以上的嵌套关系，我们可以设置最外层的根路由为<code>&quot;/&quot;</code>，加上其他嵌套子路由配置为：</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token comment">// 配置路由信息</span>
<span class="token comment">// 每个路由应该映射一个组件。 其中&quot;component&quot; 可以是</span>
<span class="token comment">// 通过 Vue.extend() 创建的组件构造器，</span>
<span class="token comment">// 或者，只是一个组件配置对象。</span>
<span class="token keyword">const</span> routes <span class="token operator">=</span> <span class="token punctuation">[</span>
  <span class="token punctuation">{</span>
    path<span class="token operator">:</span> <span class="token string">&quot;/&quot;</span><span class="token punctuation">,</span> <span class="token comment">// 父路由路径</span>
    component<span class="token operator">:</span> App<span class="token punctuation">,</span> <span class="token comment">// 父路由组件，传入 vue component</span>
    name<span class="token operator">:</span> <span class="token string">&quot;App&quot;</span><span class="token punctuation">,</span> <span class="token comment">// 路由名称</span>
    <span class="token comment">// 设置子路由</span>
    children<span class="token operator">:</span> <span class="token punctuation">[</span>
      <span class="token punctuation">{</span>
        path<span class="token operator">:</span> <span class="token string">&quot;login&quot;</span><span class="token punctuation">,</span> <span class="token comment">// 子路由路径</span>
        component<span class="token operator">:</span> Login<span class="token punctuation">,</span> <span class="token comment">// 子路由组件，会替换父组件中&lt;router-view&gt;中的内容</span>
        name<span class="token operator">:</span> <span class="token string">&quot;Login&quot;</span> <span class="token comment">// 路由名称</span>
      <span class="token punctuation">}</span><span class="token punctuation">,</span>
      <span class="token punctuation">{</span>
        <span class="token comment">// 应用首页</span>
        path<span class="token operator">:</span> <span class="token string">&quot;home&quot;</span><span class="token punctuation">,</span>
        component<span class="token operator">:</span> Home<span class="token punctuation">,</span>
        name<span class="token operator">:</span> <span class="token string">&quot;Home&quot;</span><span class="token punctuation">,</span>
        children<span class="token operator">:</span> <span class="token punctuation">[</span>
          <span class="token comment">// 服务列表</span>
          <span class="token punctuation">{</span> path<span class="token operator">:</span> <span class="token string">&quot;service&quot;</span><span class="token punctuation">,</span> component<span class="token operator">:</span> Service<span class="token punctuation">,</span> name<span class="token operator">:</span> <span class="token string">&quot;Service&quot;</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
          <span class="token comment">// 产品容器</span>
          <span class="token punctuation">{</span>
            path<span class="token operator">:</span> <span class="token string">&quot;product&quot;</span><span class="token punctuation">,</span>
            component<span class="token operator">:</span> Product<span class="token punctuation">,</span>
            name<span class="token operator">:</span> <span class="token string">&quot;Product&quot;</span><span class="token punctuation">,</span>
            children<span class="token operator">:</span> <span class="token punctuation">[</span>
              <span class="token comment">// 子路由内容</span>
              <span class="token comment">// 产品列表</span>
              <span class="token punctuation">{</span> path<span class="token operator">:</span> <span class="token string">&quot;list&quot;</span><span class="token punctuation">,</span> component<span class="token operator">:</span> ProductList<span class="token punctuation">,</span> name<span class="token operator">:</span> <span class="token string">&quot;ProductList&quot;</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
              <span class="token comment">// 产品新增</span>
              <span class="token punctuation">{</span> path<span class="token operator">:</span> <span class="token string">&quot;add/0&quot;</span><span class="token punctuation">,</span> component<span class="token operator">:</span> ProductEdit<span class="token punctuation">,</span> name<span class="token operator">:</span> <span class="token string">&quot;ProductAdd&quot;</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
              <span class="token comment">// 产品编辑</span>
              <span class="token comment">// 我们能看到，新增和编辑其实最终使用的是同一个组件，所以后面会有一些需要兼容处理的地方</span>
              <span class="token comment">// :id可匹配任意值，且可在组件中通过this.$route.params.id获取该值</span>
              <span class="token punctuation">{</span> path<span class="token operator">:</span> <span class="token string">&quot;edit/:id&quot;</span><span class="token punctuation">,</span> component<span class="token operator">:</span> ProductEdit<span class="token punctuation">,</span> name<span class="token operator">:</span> <span class="token string">&quot;ProductEdit&quot;</span> <span class="token punctuation">}</span>
            <span class="token punctuation">]</span>
          <span class="token punctuation">}</span>
        <span class="token punctuation">]</span>
      <span class="token punctuation">}</span>
    <span class="token punctuation">]</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">]</span><span class="token punctuation">;</span>
</code></pre></div><h3 id="_2-vue-中加载-vue-router-和路由信息"><a href="#_2-vue-中加载-vue-router-和路由信息" class="header-anchor">#</a> 2. Vue 中加载 vue-router 和路由信息</h3> <p>路由配置设计好之后，我们可以通过将 router 配置参数注入路由，让整个应用都有路由功能：</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token comment">// 加载路由信息</span>
<span class="token keyword">const</span> router <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">VueRouter</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
  <span class="token comment">// mode: 路由模式，'hash' | 'history'</span>
  <span class="token comment">// routes：传入路由配置信息，后面会讲怎么配置</span>
  routes <span class="token comment">// （缩写）相当于 routes: routes</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// 启动一个 Vue 应用</span>
<span class="token keyword">new</span> <span class="token class-name">Vue</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
  el<span class="token operator">:</span> <span class="token string">&quot;#app&quot;</span><span class="token punctuation">,</span>
  router<span class="token punctuation">,</span> <span class="token comment">// 传入路由能力</span>
  <span class="token function-variable function">render</span><span class="token operator">:</span> <span class="token parameter">h</span> <span class="token operator">=&gt;</span> <span class="token function">h</span><span class="token punctuation">(</span>App<span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre></div><p>这里的<a href="https://router.vuejs.org/zh/api/#mode" target="_blank" rel="noopener noreferrer">路由模式-mode<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a>包括两种：</p> <ul><li><code>hash</code> <ul><li>使用 URL hash 值来作路由。支持所有浏览器，包括不支持 HTML5 History Api 的浏览器</li> <li>例如上面说的<code>a.com/#/pageone</code>，便是 hash 模式</li></ul></li> <li><code>history</code> <ul><li>充分利用 history.pushState API 来完成 URL 跳转而无须重新加载页面</li> <li>例如<code>a.com/pageone</code>，如果觉得 hash 模式丑可以使用这种</li> <li>**注意！！**依赖 <a href="https://router.vuejs.org/zh/guide/essentials/history-mode.html#html5-history-%E6%A8%A1%E5%BC%8F" target="_blank" rel="noopener noreferrer">HTML5 History API 和服务器配置<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li></ul></li></ul> <p>到这里，我们路由配置和启动的部分已经完成，可以在 <a href="https://github.com/godbasin/vue-element-demo/blob/master/3/src/main.js" target="_blank" rel="noopener noreferrer">main.js<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a> 文件查看完整代码。</p> <h3 id="_3-router-view-使用"><a href="#_3-router-view-使用" class="header-anchor">#</a> 3. <code>&lt;router-view&gt;</code>使用</h3> <p><code>&lt;router-view&gt;</code>组件是一个 functional 组件，渲染路径匹配到的视图组件。它渲染的组件还可以内嵌自己的<code>&lt;router-view&gt;</code>，根据嵌套路径，渲染嵌套组件。</p> <p>我们来看看<code>&lt;router-view&gt;</code>的使用，这里以<code>App.vue</code>和<code>Home.vue</code>作为例子：</p> <div class="language-html extra-class"><pre class="language-html"><code><span class="token comment">&lt;!-- 这里是最外层 /app 路由的组件，App.vue --&gt;</span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>template</span><span class="token punctuation">&gt;</span></span>
  <span class="token comment">&lt;!-- 使用 &lt;router-view&gt;&lt;/router-view&gt; 来嵌套路由 --&gt;</span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>router-view</span><span class="token punctuation">&gt;</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>router-view</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>template</span><span class="token punctuation">&gt;</span></span>

<span class="token comment">&lt;!-- 这里是 /app/home 路由的组件，Home.vue --&gt;</span>
<span class="token comment">&lt;!-- 这里采用了简写，省略了一些非关键内容，更多内容可以参考上一节 --&gt;</span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>template</span><span class="token punctuation">&gt;</span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>el-container</span><span class="token punctuation">&gt;</span></span>
    <span class="token comment">&lt;!-- 左侧菜单, Menu.vue --&gt;</span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>menu</span><span class="token punctuation">&gt;</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>menu</span><span class="token punctuation">&gt;</span></span>
    <span class="token comment">&lt;!-- 右侧内容 --&gt;</span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>el-container</span><span class="token punctuation">&gt;</span></span>
      <span class="token comment">&lt;!-- 上边的头部栏 --&gt;</span>
      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>el-header</span><span class="token punctuation">&gt;</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>el-header</span><span class="token punctuation">&gt;</span></span>
      <span class="token comment">&lt;!-- 子路由页面的内容 --&gt;</span>
      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>router-view</span><span class="token punctuation">&gt;</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>router-view</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>el-container</span><span class="token punctuation">&gt;</span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>el-container</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>template</span><span class="token punctuation">&gt;</span></span>
</code></pre></div><h3 id="_4-router-link-使用"><a href="#_4-router-link-使用" class="header-anchor">#</a> 4. <code>&lt;router-link&gt;</code>使用</h3> <p><a href="/front-end-playground/vue/vue-for-everyone/vue-for-everyone-2.html">上一节内容</a>中我们拼了这么一个页面：
<img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/vue-for-everyone-2-1.jpg" alt=""></p> <p>大家<a href="http://vue-for-everyone.godbasin.com/2/index.html" target="_blank" rel="noopener noreferrer">查看页面效果<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a>的时候会发现，左侧的菜单点击没有什么反应，因为我们还没有加上路由。那么现在就使用这里的菜单，来展示下<code>&lt;router-link&gt;</code>的使用吧。</p> <div class="language-html extra-class"><pre class="language-html"><code><span class="token comment">&lt;!-- 这里是 Menu.vue，即上一节内容种拼的左侧菜单 --&gt;</span>
<span class="token comment">&lt;!-- 这里主要针对路由相关内容，更多的注释省略了，有需要可查看最终代码 --&gt;</span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>template</span><span class="token punctuation">&gt;</span></span>
  <span class="token comment">&lt;!-- 此处有个 default-active 属性需要注意，是用来设置菜单的选中样式，我们需要根据当前路由情况来选中 --&gt;</span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>el-menu</span>
    <span class="token attr-name">:collapse</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>isMenuCollapse<span class="token punctuation">&quot;</span></span>
    <span class="token attr-name">:default-openeds</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>[<span class="token punctuation">'</span>0<span class="token punctuation">'</span>, <span class="token punctuation">'</span>1<span class="token punctuation">'</span>]<span class="token punctuation">&quot;</span></span>
    <span class="token attr-name">:default-active</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>activeIndex<span class="token punctuation">&quot;</span></span>
  <span class="token punctuation">&gt;</span></span>
    <span class="token comment">&lt;!-- 遍历生成父菜单选项 --&gt;</span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>template</span> <span class="token attr-name">v-for</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>menu in menus<span class="token punctuation">&quot;</span></span><span class="token punctuation">&gt;</span></span>
      <span class="token comment">&lt;!-- 有子菜单的时候 --&gt;</span>
      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>el-submenu</span>
        <span class="token attr-name">v-if</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>menu.subMenus &amp;&amp; menu.subMenus.length<span class="token punctuation">&quot;</span></span>
        <span class="token attr-name">:index</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>menu.index<span class="token punctuation">&quot;</span></span>
        <span class="token attr-name">:key</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>menu.index<span class="token punctuation">&quot;</span></span>
      <span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>template</span> <span class="token attr-name">slot</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>title<span class="token punctuation">&quot;</span></span><span class="token punctuation">&gt;</span></span>
          <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>i</span> <span class="token attr-name">:class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>menu.icon<span class="token punctuation">&quot;</span></span><span class="token punctuation">&gt;</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>i</span><span class="token punctuation">&gt;</span></span>
          <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>span</span> <span class="token attr-name">slot</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>title<span class="token punctuation">&quot;</span></span><span class="token punctuation">&gt;</span></span>{{menu.text}}<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>span</span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>template</span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>el-menu-item-group</span><span class="token punctuation">&gt;</span></span>
          <span class="token comment">&lt;!-- 使用 router-link 组件来导航. --&gt;</span>
          <span class="token comment">&lt;!-- 通过传入 `to` 属性指定链接. --&gt;</span>
          <span class="token comment">&lt;!-- &lt;router-link&gt; 默认会被渲染成一个 `&lt;a&gt;` 标签 --&gt;</span>
          <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>router-link</span>
            <span class="token attr-name">tag</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>div<span class="token punctuation">&quot;</span></span>
            <span class="token attr-name">v-for</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>subMenu in menu.subMenus<span class="token punctuation">&quot;</span></span>
            <span class="token attr-name">:key</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>subMenu.index<span class="token punctuation">&quot;</span></span>
            <span class="token attr-name">:to</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>{name: subMenu.routerName}<span class="token punctuation">&quot;</span></span>
          <span class="token punctuation">&gt;</span></span>
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>el-menu-item</span> <span class="token attr-name">:index</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>subMenu.index<span class="token punctuation">&quot;</span></span><span class="token punctuation">&gt;</span></span>{{subMenu.text}}<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>el-menu-item</span><span class="token punctuation">&gt;</span></span>
          <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>router-link</span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>el-menu-item-group</span><span class="token punctuation">&gt;</span></span>
      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>el-submenu</span><span class="token punctuation">&gt;</span></span>
      <span class="token comment">&lt;!-- 只有单个父菜单的时候，也要给这个父菜单添加路由，同样的 to 指向要去的地方 --&gt;</span>
      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>router-link</span>
        <span class="token attr-name">v-else</span>
        <span class="token attr-name">:index</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>menu.index<span class="token punctuation">&quot;</span></span>
        <span class="token attr-name">:key</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>menu.index<span class="token punctuation">&quot;</span></span>
        <span class="token attr-name">tag</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>div<span class="token punctuation">&quot;</span></span>
        <span class="token attr-name">:to</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>{name: menu.routerName}<span class="token punctuation">&quot;</span></span>
      <span class="token punctuation">&gt;</span></span>
        <span class="token comment">&lt;!-- 没子菜单的时候，就用 el-menu-item，也要绑个序号 index --&gt;</span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>el-menu-item</span><span class="token punctuation">&gt;</span></span>
          <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>i</span> <span class="token attr-name">:class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>menu.icon<span class="token punctuation">&quot;</span></span><span class="token punctuation">&gt;</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>i</span><span class="token punctuation">&gt;</span></span>
          <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>span</span> <span class="token attr-name">slot</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>title<span class="token punctuation">&quot;</span></span><span class="token punctuation">&gt;</span></span>{{menu.text}}<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>span</span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>el-menu-item</span><span class="token punctuation">&gt;</span></span>
      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>router-link</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>template</span><span class="token punctuation">&gt;</span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>el-menu</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>template</span><span class="token punctuation">&gt;</span></span>
</code></pre></div><p><code>&lt;router-link&gt;</code>的使用，除了通过 name 来跳转之外，还可通过 path 跳转、带上参数、激活样式、tag 设置等：</p> <ul><li>to: 一个路径字符串, 或者一个对象 location descriptor</li> <li>tag: 渲染成的 html 元素类型，默认是</li> <li>exact: 用于控制当前激活项的行为</li> <li>append: 控制相对链接路径的追加方式</li> <li>replace: 替代而不是作为历史条目压榨</li> <li>active-class: 当链接项激活时增加的 CSS 样式</li></ul> <p>更多的大家可以参考<a href="https://router.vuejs.org/zh/api/#router-link" target="_blank" rel="noopener noreferrer">官网 router-link API<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a>。也可以在 <a href="https://github.com/godbasin/vue-element-demo/blob/master/3/src/components/Menu.vue" target="_blank" rel="noopener noreferrer">Menu.vue<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a> 文件查看 Menu 组件的完整代码。</p> <h3 id="_5-使用-watch-监控路由变化"><a href="#_5-使用-watch-监控路由变化" class="header-anchor">#</a> 5. 使用 watch 监控路由变化</h3> <p>对应的，我们需要在 menus 里加上 routerName，用来跳转：</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token comment">// routerName 为对应的路由的路由名称</span>
<span class="token keyword">const</span> menus <span class="token operator">=</span> <span class="token punctuation">[</span>
  <span class="token punctuation">{</span>
    text<span class="token operator">:</span> <span class="token string">&quot;服务管理&quot;</span><span class="token punctuation">,</span>
    icon<span class="token operator">:</span> <span class="token string">&quot;el-icon-setting&quot;</span><span class="token punctuation">,</span>
    subMenus<span class="token operator">:</span> <span class="token punctuation">[</span><span class="token punctuation">{</span> text<span class="token operator">:</span> <span class="token string">&quot;服务信息&quot;</span><span class="token punctuation">,</span> routerName<span class="token operator">:</span> <span class="token string">&quot;Service&quot;</span> <span class="token punctuation">}</span><span class="token punctuation">]</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>
  <span class="token punctuation">{</span>
    text<span class="token operator">:</span> <span class="token string">&quot;产品管理&quot;</span><span class="token punctuation">,</span>
    icon<span class="token operator">:</span> <span class="token string">&quot;el-icon-menu&quot;</span><span class="token punctuation">,</span>
    subMenus<span class="token operator">:</span> <span class="token punctuation">[</span>
      <span class="token punctuation">{</span> text<span class="token operator">:</span> <span class="token string">&quot;产品信息&quot;</span><span class="token punctuation">,</span> routerName<span class="token operator">:</span> <span class="token string">&quot;ProductList&quot;</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
      <span class="token punctuation">{</span> text<span class="token operator">:</span> <span class="token string">&quot;新增&quot;</span><span class="token punctuation">,</span> routerName<span class="token operator">:</span> <span class="token string">&quot;ProductAdd&quot;</span> <span class="token punctuation">}</span>
    <span class="token punctuation">]</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>
  <span class="token comment">// 日志信息这里为空，则不会进行跳转</span>
  <span class="token punctuation">{</span> text<span class="token operator">:</span> <span class="token string">&quot;日志信息&quot;</span><span class="token punctuation">,</span> icon<span class="token operator">:</span> <span class="token string">&quot;el-icon-message&quot;</span><span class="token punctuation">,</span> routerName<span class="token operator">:</span> <span class="token string">&quot;&quot;</span> <span class="token punctuation">}</span>
<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">x<span class="token punctuation">,</span> i</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token comment">// 添加 index，可用于默认展开 default-openeds 属性，和激活状态 efault-active 属性的设置</span>
  <span class="token keyword">return</span> <span class="token punctuation">{</span>
    <span class="token operator">...</span>x<span class="token punctuation">,</span>
    <span class="token comment">// 子菜单就拼接${父菜单index}-${子菜单index}</span>
    subMenus<span class="token operator">:</span> <span class="token punctuation">(</span>x<span class="token punctuation">.</span>subMenus <span class="token operator">||</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">y<span class="token punctuation">,</span> j</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
      <span class="token keyword">return</span> <span class="token punctuation">{</span> <span class="token operator">...</span>y<span class="token punctuation">,</span> index<span class="token operator">:</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>i<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">-</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>j<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span> <span class="token punctuation">}</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
    <span class="token comment">// 父菜单就把 index 加上好了</span>
    index<span class="token operator">:</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>i<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span>
  <span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre></div><p>根据<a href="https://element.eleme.io/#/zh-CN/component/menu#menu-attribute" target="_blank" rel="noopener noreferrer">el-menu 的配置<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a>我们知道，<code>&lt;el-menu&gt;</code>的<code>default-active</code>属性需要设置当前激活菜单的 index，因此我们需要监控路由的变化，并根据路由情况调整绑定的激活 index。</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token comment">// 下面是 Vue 组件</span>
<span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token punctuation">{</span>
  <span class="token function">data</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> <span class="token punctuation">{</span>
      menus<span class="token punctuation">,</span> <span class="token comment">// menus: menus 的简写</span>
      activeIndex<span class="token operator">:</span> <span class="token string">&quot;&quot;</span>
    <span class="token punctuation">}</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>
  watch<span class="token operator">:</span> <span class="token punctuation">{</span>
    <span class="token comment">// 为了设置 el-menu 的 default-active 属性，需要获取到路由状态</span>
    <span class="token function">$route</span><span class="token punctuation">(</span><span class="token parameter">to</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token comment">// 对路由变化作出响应...</span>
      <span class="token keyword">let</span> activeIndex<span class="token punctuation">;</span>
      <span class="token comment">// 找到匹配的 routerName</span>
      <span class="token keyword">this</span><span class="token punctuation">.</span>menus<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token parameter">x</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span>x<span class="token punctuation">.</span>routerName <span class="token operator">===</span> to<span class="token punctuation">.</span>name<span class="token punctuation">)</span> <span class="token punctuation">{</span>
          activeIndex <span class="token operator">=</span> x<span class="token punctuation">.</span>index<span class="token punctuation">;</span>
        <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
          <span class="token keyword">const</span> subMenuItem <span class="token operator">=</span> x<span class="token punctuation">.</span>subMenus<span class="token punctuation">.</span><span class="token function">find</span><span class="token punctuation">(</span><span class="token parameter">y</span> <span class="token operator">=&gt;</span> y<span class="token punctuation">.</span>routerName <span class="token operator">===</span> to<span class="token punctuation">.</span>name<span class="token punctuation">)</span><span class="token punctuation">;</span>
          <span class="token keyword">if</span> <span class="token punctuation">(</span>subMenuItem<span class="token punctuation">)</span> <span class="token punctuation">{</span>
            activeIndex <span class="token operator">=</span> subMenuItem<span class="token punctuation">.</span>index<span class="token punctuation">;</span>
          <span class="token punctuation">}</span>
        <span class="token punctuation">}</span>
      <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token comment">// 并将 activeIndex 设置为对应的 菜单 index</span>
      <span class="token keyword">if</span> <span class="token punctuation">(</span>activeIndex<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">this</span><span class="token punctuation">.</span>activeIndex <span class="token operator">=</span> activeIndex<span class="token punctuation">;</span>
      <span class="token punctuation">}</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
</code></pre></div><p>我们看到，这里使用了一个叫 watch 的属性。Vue 中监听属性 watch 和计算属性 computed 也是很常用的能力（能节省不少的代码），衍生的其他依赖状态就可以使用 computed 来处理，而某种状态的变更可以使用 watch 监听。这里篇幅关系不多说，大家可以去翻阅下<a href="(https://cn.vuejs.org/v2/guide/computed.html)">官方文档</a>。</p> <h3 id="_6-路由跳转"><a href="#_6-路由跳转" class="header-anchor">#</a> 6. 路由跳转</h3> <p>除了使用<code>&lt;router-link&gt;</code>来定义导航链接，我们还可以借助 router 的实例方法，通过编写代码来实现。在 Vue 实例内部，我们可以通过 $router 访问路由实例，例如我们在 ProductList 页面需要跳转到 ProductEdit 页面来编辑/新增选项内容：</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token punctuation">{</span>
  <span class="token comment">// ...其他省略</span>
  methods<span class="token operator">:</span> <span class="token punctuation">{</span>
    <span class="token comment">// 新增/修改一个数据</span>
    <span class="token function">updateTableItem</span><span class="token punctuation">(</span><span class="token parameter">id <span class="token operator">=</span> <span class="token number">0</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token comment">// 跳转到编辑页面，新增则传id为0，否则为编辑</span>
      <span class="token comment">// 可以通过 this.$router 访问路由实例</span>
      <span class="token keyword">if</span> <span class="token punctuation">(</span>id <span class="token operator">!==</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token comment">// 传参 name 为路由名字，params 为我们定义的路由 path 的参数，变成 /edit/xxx</span>
        <span class="token comment">// 还有另外一种传参方式 query，带查询参数，变成 /edit?id=xxx</span>
        <span class="token keyword">this</span><span class="token punctuation">.</span>$router<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span><span class="token punctuation">{</span> name<span class="token operator">:</span> <span class="token string">&quot;ProductEdit&quot;</span><span class="token punctuation">,</span> params<span class="token operator">:</span> <span class="token punctuation">{</span> id <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
        <span class="token keyword">this</span><span class="token punctuation">.</span>$router<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span><span class="token punctuation">{</span> name<span class="token operator">:</span> <span class="token string">&quot;ProductAdd&quot;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token punctuation">}</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
</code></pre></div><p>router 实例的使用和<code>&lt;router-link&gt;</code>其实很相像，也挺简单的，可以参考<a href="https://router.vuejs.org/zh/guide/essentials/navigation.html" target="_blank" rel="noopener noreferrer">编程式的导航<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a>。</p> <h2 id="给路由添加鉴权"><a href="#给路由添加鉴权" class="header-anchor">#</a> 给路由添加鉴权</h2> <p>既然我们这一次设计了登录页和应用首页（请各位根据<a href="/front-end-playground/vue/vue-for-everyone/vue-for-everyone-2.html">第二节内容</a>分别自行拼出来），一般来说，我们会设计只有当登录完成之后，才可以进入应用里面的其他页面。</p> <h3 id="_1-设置简单的全局数据"><a href="#_1-设置简单的全局数据" class="header-anchor">#</a> 1. 设置简单的全局数据</h3> <p>一般来说，在 Vue 中会使用 Vuex 来管理数据状态。基于本节内容主要讲 vue-router，所以我们简单设计一个全局数据的管理库：</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token comment">// globalData.js</span>
<span class="token comment">// globalData 用来存全局数据</span>
<span class="token keyword">let</span> globalData <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token comment">// 获取全局数据</span>
<span class="token comment">// 传 key 获取对应的值</span>
<span class="token comment">// 不传 key 获取全部值</span>
<span class="token keyword">export</span> <span class="token keyword">function</span> <span class="token function">getGlobalData</span><span class="token punctuation">(</span><span class="token parameter">key</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">return</span> key <span class="token operator">?</span> globalData<span class="token punctuation">[</span>key<span class="token punctuation">]</span> <span class="token operator">:</span> globalData<span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token comment">// 设置全局数据</span>
<span class="token keyword">export</span> <span class="token keyword">function</span> <span class="token function">setGlobalData</span><span class="token punctuation">(</span><span class="token parameter">key<span class="token punctuation">,</span> value</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token comment">// 需要传键值对</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span>key <span class="token operator">===</span> <span class="token keyword">undefined</span> <span class="token operator">||</span> value <span class="token operator">===</span> <span class="token keyword">undefined</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
  globalData <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token operator">...</span>globalData<span class="token punctuation">,</span> <span class="token punctuation">[</span>key<span class="token punctuation">]</span><span class="token operator">:</span> value <span class="token punctuation">}</span><span class="token punctuation">;</span>
  <span class="token keyword">return</span> globalData<span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token comment">// 清除全局数据</span>
<span class="token comment">// 传 key 清除对应的值</span>
<span class="token comment">// 不传 key 清除全部值</span>
<span class="token keyword">export</span> <span class="token keyword">function</span> <span class="token function">clearGlobalData</span><span class="token punctuation">(</span><span class="token parameter">key</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token comment">// 需要传键值对</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span>key <span class="token operator">===</span> <span class="token keyword">undefined</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    globalData <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
    <span class="token keyword">delete</span> globalData<span class="token punctuation">[</span>key<span class="token punctuation">]</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
  <span class="token keyword">return</span> globalData<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre></div><p>使用这种方式的全局数据，是会在页面刷新之后丢失的。而如果用来存用户的登录态信息，为了避免频繁登录，更好的方式是存到 cookie 或者缓存里。</p> <h3 id="_2-登录页面登录"><a href="#_2-登录页面登录" class="header-anchor">#</a> 2. 登录页面登录</h3> <p>拼好的页面可以查看 <a href="https://github.com/godbasin/vue-element-demo/blob/master/3/src/pages/Login.vue" target="_blank" rel="noopener noreferrer">Login.vue<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a> 文件，这里由于篇幅关系，我们只看保存数据和跳转的部分：</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token keyword">import</span> <span class="token punctuation">{</span> setGlobalData <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">&quot;utils/globalData&quot;</span><span class="token punctuation">;</span>

<span class="token comment">// 下面是 Vue 组件</span>
<span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token punctuation">{</span>
  <span class="token comment">// ...其他省略</span>
  methods<span class="token operator">:</span> <span class="token punctuation">{</span>
    <span class="token comment">// 提交新增/修改表单</span>
    <span class="token function">onSubmit</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token comment">// 校验表单，用户名和密码都必须填入</span>
      <span class="token comment">// Element 表单校验规则配置，请查看https://element.eleme.cn/#/zh-CN/component/form</span>
      <span class="token keyword">this</span><span class="token punctuation">.</span>$refs<span class="token punctuation">[</span><span class="token string">&quot;form&quot;</span><span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token function">validate</span><span class="token punctuation">(</span><span class="token parameter">valid</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span>valid<span class="token punctuation">)</span> <span class="token punctuation">{</span>
          <span class="token comment">// 校验通过</span>
          <span class="token comment">// 设置用户名</span>
          <span class="token function">setGlobalData</span><span class="token punctuation">(</span><span class="token string">&quot;username&quot;</span><span class="token punctuation">,</span> <span class="token keyword">this</span><span class="token punctuation">.</span>form<span class="token punctuation">.</span>username<span class="token punctuation">)</span><span class="token punctuation">;</span>
          <span class="token comment">// 跳转到里页</span>
          <span class="token keyword">this</span><span class="token punctuation">.</span>$router<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span><span class="token punctuation">{</span> name<span class="token operator">:</span> <span class="token string">&quot;Home&quot;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
          <span class="token comment">// 校验失败</span>
          <span class="token keyword">return</span> <span class="token boolean">false</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
      <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
</code></pre></div><h3 id="_3-鉴权进入内页"><a href="#_3-鉴权进入内页" class="header-anchor">#</a> 3. 鉴权进入内页</h3> <p>这里，我们需要使用 vue-router 的<a href="https://router.vuejs.org/zh/guide/advanced/navigation-guards.html#%E5%85%A8%E5%B1%80%E5%89%8D%E7%BD%AE%E5%AE%88%E5%8D%AB" target="_blank" rel="noopener noreferrer">导航守卫<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a>能力，当用户未登录时，则拒绝进入其他路由页面里：</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token comment">// main.js</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> getGlobalData <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">&quot;utils/globalData&quot;</span><span class="token punctuation">;</span>

router<span class="token punctuation">.</span><span class="token function">beforeEach</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">to<span class="token punctuation">,</span> from<span class="token punctuation">,</span> next</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span>to<span class="token punctuation">.</span>name <span class="token operator">!==</span> <span class="token string">&quot;Login&quot;</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token comment">// 非 login 页面，检查是否登录</span>
    <span class="token comment">// 这里简单前端模拟是否填写了用户名，真实环境需要走后台登录，缓存到本地</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span><span class="token function">getGlobalData</span><span class="token punctuation">(</span><span class="token string">&quot;username&quot;</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token function">next</span><span class="token punctuation">(</span><span class="token punctuation">{</span> name<span class="token operator">:</span> <span class="token string">&quot;Login&quot;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span>
  <span class="token comment">// 其他情况正常执行</span>
  <span class="token function">next</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre></div><p>到这里，我们整个应用可以顺利地跑起来了：</p> <ul><li><a href="http://vue-for-everyone.godbasin.com/3/index.html" target="_blank" rel="noopener noreferrer">页面的效果查看<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li> <li><a href="https://github.com/godbasin/vue-element-demo/tree/master/3" target="_blank" rel="noopener noreferrer">页面代码查看<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li></ul> <h1 id="结束语"><a href="#结束语" class="header-anchor">#</a> 结束语</h1> <p>很多人会谈到程序员的天花板，前端开发也喜欢谈前端开发的天花板。我们总以为自己所经历的是最艰难的，其实每个人都是一样的。工地的建筑工人、快递外卖小哥、各行各业都有各自的难处，人只要想要往上走，总是要爬坡的。<br>
自我感动取之便捷，又容易上瘾，我们绝不能走到半山腰上就嚎啕大哭。</p></div> <!----> <footer class="page-edit"><div class="edit-link"><a href="https://github.com/godbasin/front-end-playground/edit/sourcecode/docs/vue/vue-for-everyone/vue-for-everyone-3.md" target="_blank" rel="noopener noreferrer">帮阿猪改善此页面！</a> <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></div> <!----> <blockquote>部分文章中使用了一些网站的截图，如果涉及侵权，请告诉我删一下谢谢~</blockquote> <div style="margin-top:30px;"><div class="el-row" style="margin-left:-10px;margin-right:-10px;"><div class="el-col el-col-24 el-col-sm-0 el-col-md-2 el-col-lg-4" style="padding-left:10px;padding-right:10px;display:block;"><div style="width:1px;height:1px;"></div></div> <div class="el-col el-col-24 el-col-sm-24 el-col-md-18 el-col-lg-16" style="padding-left:10px;padding-right:10px;"><div class="el-card box-card is-always-shadow"><div class="el-card__header"><div class="clearfix"><span>温馨提示喵</span></div></div><div class="el-card__body"> <div class="el-row" style="margin-left:-10px;margin-right:-10px;"><div class="el-col el-col-24 el-col-xs-24 el-col-sm-12" style="padding-left:10px;padding-right:10px;"><div class="el-image"><div class="image-slot"><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/assets/img/loading.gif" style="width:100%;"></div><!----></div></div> <div class="el-col el-col-24 el-col-xs-24 el-col-sm-12" style="padding-left:10px;padding-right:10px;"><div class="copyright-text"><div>本文版权归作者所有，欢迎转载，但未经作者同意必须保留此段声明，且在文章页面明显位置给出原文连接，否则保留追究法律责任的权利。</div> <div>出处：被删的前端游乐场</div> <div>作者：<a href="https://github.com/godbasin" target="_blank">被删</a></div></div></div></div></div></div></div></div></div></footer> <div class="page-nav"><p class="inner"><span class="prev">
        ←
        <a href="/front-end-playground/vue/vue-for-everyone/vue-for-everyone-2.html" class="prev">
          2. 怎么三两下拼出一个页面
        </a></span> <span class="next"><a href="/front-end-playground/vue/vue-transition/1-transition-component.html">
          1. transition组件
        </a>
        →
      </span></p></div>  <div class="gitalk-container theme-default-content"><div id="gitalk-container" class="content"></div></div></main> <div id="kitty-container"><span><div role="tooltip" id="el-popover-3982" aria-hidden="true" class="el-popover el-popper" style="width:undefinedpx;display:none;"><!----><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/2code2.jpg" class="image"> <div class="text">牡羊猪的猫粮罐</div> </div><span class="el-popover__reference-wrapper"><div id="kitty" style="background:url(https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/assets/img/kitty1.svg);"></div></span></span> <div class="el-dialog__wrapper" style="display:none;"><div role="dialog" aria-modal="true" aria-label="牡羊猪是这样渐渐胖成猪的喵（点击图片可以切换噢）" class="el-dialog" style="margin-top:15vh;"><div class="el-dialog__header"><span class="el-dialog__title">牡羊猪是这样渐渐胖成猪的喵（点击图片可以切换噢）</span><button type="button" aria-label="Close" class="el-dialog__headerbtn"><i class="el-dialog__close el-icon el-icon-close"></i></button></div><!----><!----></div></div></div></div><div class="global-ui"></div></div>
    <script src="/front-end-playground/assets/js/app.1e2670bf.js" defer></script><script src="/front-end-playground/assets/js/2.38d016d1.js" defer></script><script src="/front-end-playground/assets/js/3.e3f029cb.js" defer></script><script src="/front-end-playground/assets/js/142.b26ccf3b.js" defer></script>
  </body>
</html>
